package com.mate.cloud.hot.service.impl;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.mate.cloud.hot.service.HotKeyCacheService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;


@Service
public class HotKeyCacheServiceImpl implements HotKeyCacheService {

    // 使用 Caffeine 作为本地缓存
    private final Cache<String, Object> localCache = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.SECONDS) // 短暂过期时间，防止数据不一致
            .maximumSize(10000)
            .build();
    // 模拟 DB
    private final Map<String, String> database = Map.of(
            "user:1001", "张三",
            "user:1002", "李四"
    );

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private Cache<String, Object> caffeineCache;
    /**
     * 获取热点数据（先本地缓存，再Redis）
     */
    @Override
    public Object getHotKey(String key) {
        // 1. 先从本地缓存查询
        Object value = localCache.getIfPresent(key);
        if (value != null) {
            return value;
        }

        // 2. 本地缓存没有，查询Redis
        value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            // 3. 将Redis数据写入本地缓存，短暂过期
            localCache.put(key, value);
        }

        return value;
    }

    /**
     * 更新热点数据（双写策略）
     */
    @Override
    public void updateHotKey(String key, Object value) {
        // 1. 更新Redis
        redisTemplate.opsForValue().set(key, value);

        // 2. 更新本地缓存
        localCache.put(key, value);
    }

    /**
     * 获取缓存（先查本地 → Redis → DB）
     */
    @Override
    public String getCache(String key) {
        // 1. 先查本地缓存
        String value = (String) caffeineCache.getIfPresent(key);
        if (value != null) {
            return value + " (from Caffeine)";
        }

        // 2. 查 Redis
        value = (String) redisTemplate.opsForValue().get(key);
        if (value != null) {
            caffeineCache.put(key, value); // 回填本地缓存
            return value + " (from Redis)";
        }

        // 3. 加锁防止缓存击穿
        synchronized (this) {
            // 双重检查 Redis
            value = (String) redisTemplate.opsForValue().get(key);
            if (value != null) {
                caffeineCache.put(key, value);
                return value + " (from Redis after lock)";
            }

            // 4. 查询 DB（模拟）
            value = database.get(key);
            if (value == null) {
                // 防止缓存穿透，写一个空值，短暂过期
                redisTemplate.opsForValue().set(key, "", 30, TimeUnit.SECONDS);
                return null;
            }

            // 5. 写 Redis + Caffeine
            redisTemplate.opsForValue().set(key, value,
                    5 + new Random().nextInt(5), TimeUnit.MINUTES); // 随机过期，防止雪崩
            caffeineCache.put(key, value);

            return value + " (from DB)";
        }
    }
}
